from subprocess import PIPE, Popen
import sys
from urllib import request
import glob
import platform
import shutil
import configparser
import os
from functools import wraps
from typing import Callable


def work_logger(log_path):
    # 记录日志
    # @func_name():装饰器的作用是使用一个函数来处理另一个函数，用来扩展被装饰的函数的功能。
    # 装饰器加了@wraps后，被装饰器装饰后的函数，还是保持原有的状态
    # 装饰器的作用: 在不改变原有功能代码的基础上,添加额外的功能。
    # @wraps(func)的作用: 不改变使用装饰器原有函数的结构

    # 参数：*args 用来将参数打包成tuple给函数体调用， **kwargs 打包关键字参数成dict给函数体调用
    # 注意点：参数arg、*args、**kwargs三个参数的位置必须是一定的。必须是(arg,*args,**kwargs)这个顺序，否则程序会报错。

    def logg_decorator(func: Callable):
        @wraps(func)
        def decorated(*args, **kwargs):
            with open(log_path, 'a+') as logf:
                logf.write(f'**** run {func.__name__}****\r\n')
            rt = func(*args, log_path=log_path, **kwargs)
            with open(log_path, 'a+') as logf:
                logf.write(f'\n\n****Completed****\r\n')
            return rt
        return(decorated)
    return logg_decorator


def run_command_sync(command, log_path=None, bufsize=-1, stdin=None,
                     stdout=PIPE, stderr=PIPE, cwd=None, silent=False) -> int:
    '''Run command and wait for return.

    '''
    args = command.split(' ')
    print(command)
    if log_path is not None:
        with open(log_path, 'a+')as log:
            log.write(f'{command}\n')
        try:
            p = Popen(args, bufsize=-1, stdin=stdin,
                      stdout=stdout, stderr=stderr, cwd=cwd)
            outs, errs = p.communicate()
            outs = outs.decode()
            errs = errs.decode()
            if log_path is not None:
                with open(log_path, 'a+') as log:
                    log.write(f'{outs}\n')
                    log.write(f'{errs}\n')
            if not silent:
                print(outs)
                print(errs)
            return p.returncode
        except Exception as e:
            if log_path is not None:
                with open(log_path, 'a+')as log:
                    log.write(f'{e}\n')
            if not silent:
                print(e)
        return -1


@work_logger(os.path.join(os.getcwd(), f'Tips\{__name__}.log'))
def make_floder(floder, log_path):
    # 创建文件夹
    with open(log_path, 'a+') as log:
        log.write('make a floder ?')
    print('make a floder ?')
    key = input('input `y` to continue, other input will be take as a no:')
    if key != 'y':
        exit(0)
    try:
        if not os.path.exists(floder):
            print('Done')
            os.makedirs(floder)
    except Exception as e:
        exit(-1)


@work_logger(os.path.join(os.getcwd(), f'Tips\{__name__}.log'))
def remove_floder(floder, log_path):
    # 删除文件夹、文件
    with open(log_path, 'a+') as log:
        log.write('delete file or floder ?')
    print('delete file or floder ?')
    key = input('input `y` to continue, other input will be take as a no:')
    if key != 'y':
        exit(0)
    if not os.path.exists(floder):
        pass
    elif os.path.isdir(floder):
        shutil.rmtree(floder)  # 递归地删除文件
    else:
        os.remove(floder)
    print('Done')


@work_logger(os.path.join(os.getcwd(), f'Tips\{__name__}.log'))
def install_sdk(floder, sdk_version, log_path):
    dotnet_root = os.path.join(floder, 'dotnet-test')
    os.environ['DOTNET_ROOT'] = dotnet_root
    sdk_dir = os.environ['DOTNET_ROOT']

    # 获得当前系统类型
    system = platform.system().lower()
    if system == 'windows':
        oss = 'win'
    elif system == 'linux':
        release_files = glob.glob('/etc/*release')
        content = ''
        for release_file in release_files:
            with open(release_file, 'r') as f:
                content += f.read().lower()
        if 'alpine' in content:
            oss = 'linux-musl'
        else:
            oss = 'linux'
    elif system == 'darwin':
        oss = 'osx'
    else:
        raise f'unsupported OS:{system}'
    print('download SDK:')
    if oss == 'win':
        script_url = 'https://dot.net/v1/dotnet-install.ps1'
        shell_name = 'powershell.exe'
    else:
        script_url = 'https://dotnet.microsoft.com/download/dotnet-core/scripts/v1/dotnet-install.sh'
        shell_name = '/bin/bash'

    req = request.urlopen(script_url)
    try:
        with open(f'{floder}/{os.path.basename(script_url)}', 'w+') as f:
            f.write(req.read().decode())
    except Exception as e:
        message = f'failed to download install script:{e}\n'
        print(message)
        with open(log_path, 'a+') as log:
            log.write(message)
        sys.exit(-1)

    rt_code = run_command_sync(
        ' '.join([f'{shell_name} {floder}/{os.path.basename(script_url)}',
                  f'-InstallDir {sdk_dir} -v {sdk_version}']), log_path=log_path)
    if rt_code == 0:
        message = 'successfully install sdk\n'
    else:
        message = 'fail to install sdk\n'
    print(message)
    with open(log_path, 'a+')as log:
        log.write(message)
    if rt_code != 0:
        sys.exit(-1)


@work_logger(os.path.join(os.getcwd(), f'Tips\{__name__}.log'))
def install_tools(floder, tool_version, tool_feed, log_path: os.PathLike = None):
    dotnet_tools = os.path.join(floder, 'dotnet_tools')
    tools = ['dotnet-counters', 'dotnet-dump',
             'dotnet-gcdump', 'dotnet-sos', 'dotnet-trace']
    print('Install .dotnet Tools:')
    for tool in tools:
        rt_code = run_command_sync(' '.join(
            [f'dotnet tool install {tool}',
             f'--tool-path {dotnet_tools}',
             f'--version {tool_version}',
             f'--add-source {tool_feed}']), log_path=log_path)
        if rt_code != 0:
            message = f'fail to install tool: {tool}!\n'
            print(message)
            with open(log_path, 'a+') as log:
                log.write(message)
            sys.exit(-1)


if __name__ == '__main__':

    # 读取配置文件
    config_file = os.path.join(os.getcwd(), 'Tips\config.conf')
    config = configparser.ConfigParser()
    config.read(config_file)
    # print(config['Test']['Testfile'])
    # print(config_file)
    TestFile = os.path.join(os.getcwd(), config['Test']['Testfile'])
    remove_floder(TestFile)
    make_floder(TestFile)
    # 安装dotnet SDK
    sdk_version = config['SDK']['Version']
    install_sdk(TestFile, sdk_version)
    # 安装dotnet tools
    tool_version = config['Tool']['Version']
    tool_feed = config['Tool']['Feed']
    install_tools(TestFile, tool_version, tool_feed)
